#include "global.h"
#include "battle.h"
#include "item.h"
#include "constants/hold_effects.h"
#include "constants/abilities.h"
#include "malloc.h"
#include "constants/battle_move_effects.h"
#include "constants/moves.h"
#include "battle_util.h"
#include "util.h"
#include "constants/battle_move_table.h"
#include "battle_ai_util.h"
#include "constants/battle_config.h"
#include "battle_ai_positivies.h"

extern u32 CheckCanKnockOff(u8 battlerDef);
extern u16 AI_GetTypeEffectiveness(u16 move, u8 battlerAtk, u8 battlerDef);
extern void UpdateMoveResultFlags(u16 modifier);
extern u32 CalcAccRatio(u32 moveAcc, u8 buff);
s32 AI_CalcDamage(u16 move, u8 battlerAtk, u8 battlerDef);
s32 AI_CalcPartyMonDamage(u16 move, u8 battlerAtk, u8 battlerDef, struct Pokemon *mon);
void MulModifier(u16 *modifier, u16 val);
s32 CalcStealthHazardDamage(u32 maxHp, u16 modifier);
bool32 CanEvolve(u32 species);
bool8 IsSpecialZCrystal(u16 item);
bool32 IsEvolutionHappened(u32 battlerId, u8 type);
u16 ConvertZMove(u16 move, u16 species, u16 itemId);

bool8 IsMagicRoomActive(void)
{
    return gFieldTimers.magicRoomTimer == 0;
}

u16 GetMonItemEffect(struct Pokemon* mon)
{
	if (GetMonAbility(mon) != ABILITY_KLUTZ && !IsMagicRoomActive())
		return ItemId_GetHoldEffect(GetMonData(mon, MON_DATA_HELD_ITEM, NULL));

	return 0;
}

u16 GetBankItemEffect(u8 bank)
{
	if (ABILITY(bank) != ABILITY_KLUTZ /*&& !gSideTimers->EmbargoTimers[bank]*/ && !IsMagicRoomActive())
		return ItemId_GetHoldEffect(ITEM(bank));

	return 0;
}
//see GetBattlerTotalSpeedStat

u16 SpeedCalcMon(u8 side, struct Pokemon* mon)
{
    u32 speed = GetMonData(mon, MON_DATA_SPEED);
    u32 ability = GetMonAbility(mon);
    u32 holdEffect = GetMonItemEffect(mon);
    u16 status1 = GetMonData(mon, MON_DATA_STATUS);

    // weather abilities
    if (WEATHER_HAS_EFFECT && HOLD_EFFECT_UTILITY_UMBRELLA != holdEffect)
    {
        if (ability == ABILITY_SWIFT_SWIM       && gBattleWeather & WEATHER_RAIN_ANY)
            speed *= 2;
        else if (ability == ABILITY_CHLOROPHYLL && gBattleWeather & WEATHER_SUN_ANY)
            speed *= 2;
        else if (ability == ABILITY_SAND_RUSH   && gBattleWeather & WEATHER_SANDSTORM_ANY)
            speed *= 2;
        else if (ability == ABILITY_SLUSH_RUSH  && gBattleWeather & WEATHER_HAIL_ANY)
            speed *= 2;
    }

    // other abilities
    if (ability == ABILITY_QUICK_FEET && status1 & STATUS1_ANY)
        speed = (speed * 150) / 100;
    else if (ability == ABILITY_SURGE_SURFER && gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)
        speed *= 2;

    // item effects
    if (holdEffect == HOLD_EFFECT_MACHO_BRACE || holdEffect == HOLD_EFFECT_EV_BOOST)
        speed /= 2;
    else if (holdEffect == HOLD_EFFECT_IRON_BALL)
        speed /= 2;
    else if (holdEffect == HOLD_EFFECT_CHOICE_SCARF)
        speed = (speed * 150) / 100;

    // various effects
    if (gSideStatuses[side] & SIDE_STATUS_TAILWIND)
        speed *= 2;

    // paralysis drop
    if (status1 & STATUS1_PARALYSIS && ability != ABILITY_QUICK_FEET)
        speed /= (B_PARALYSIS_SPEED >= GEN_7 ? 2 : 4);

    return speed;
}

bool8 IsGravityActive()
{
    return gFieldStatuses & STATUS_FIELD_GRAVITY;
}

bool8 CheckGroundingFromPartyData(struct Pokemon* mon)
{
	u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
	u16 item = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);

	if (IsGravityActive()
	|| (ItemId_GetHoldEffect(item) == HOLD_EFFECT_IRON_BALL && GetMonAbility(mon) != ABILITY_KLUTZ))
		return 1;

	else if  (GetMonAbility(mon) == ABILITY_LEVITATE
		|| gBaseStats[species].type1 == TYPE_FLYING
		|| gBaseStats[species].type2 == TYPE_FLYING)
			return 0;
	return 1;
}

void PopulateDamageCalcStructWithBaseAttackerData(struct DamageCalc* data)
{
	u8 bankAtk = data->bankAtk;
	bool8 useMonAtk = data->monAtk != NULL;

	if (useMonAtk)
	{
		struct Pokemon* monAtk = data->monAtk;

		data->atkSpecies = GetMonData(monAtk, MON_DATA_SPECIES);
		data->atkAbility = GetMonAbility(monAtk);
		data->atkPartnerAbility = ABILITY_NONE;
		data->atkItemEffect = GetMonItemEffect(monAtk);
		data->atkItem = GetMonData(monAtk, MON_DATA_HELD_ITEM);
		data->atkItemQuality = ItemId_GetHoldEffectParam(data->atkItem);
		data->atkHP = monAtk->hp;
		data->atkMaxHP = monAtk->maxHP;
		data->atkSpeed = SpeedCalcMon(SIDE(bankAtk), monAtk);
		data->atkStatus1 = GetMonData(monAtk, MON_DATA_STATUS);
		data->atkStatus3 = 0;
		data->atkIsGrounded = CheckGroundingFromPartyData(monAtk);
	}
	else //Load from bank
	{
		data->atkSpecies = SPECIES(bankAtk);
		data->atkAbility = ABILITY(bankAtk);
		if (IS_DOUBLE_BATTLE && BATTLER_ALIVE(PARTNER(bankAtk)))
			data->atkPartnerAbility = ABILITY(PARTNER(bankAtk));
		else
			data->atkPartnerAbility = 0;
		data->atkItem = ITEM(bankAtk);
		data->atkItemEffect = ITEM_EFFECT(bankAtk);
		data->atkItemQuality = ITEM_QUALITY(bankAtk);

		data->atkHP = gBattleMons[bankAtk].hp;
		data->atkMaxHP = gBattleMons[bankAtk].maxHP;
		data->atkSpeed = SpeedCalc(bankAtk);
		data->atkStatus1 = gBattleMons[bankAtk].status1;
		data->atkStatus3 = gStatuses3[bankAtk];
		data->atkIsGrounded = CheckGrounding(bankAtk);
	}
	
	data->attackerLoaded = TRUE;
}

bool8 IsWonderRoomActive()
{
    return gFieldStatuses & STATUS_FIELD_WONDER_ROOM;
}

void PopulateDamageCalcStructWithBaseDefenderData(struct DamageCalc* data)
{
	bool8 useMonDef = data->monDef != NULL;
	u8 bankDef = data->bankDef;

	if (useMonDef)
	{
		struct Pokemon* monDef = data->monDef;

		data->defSpecies = GetMonData(monDef, MON_DATA_SPECIES);
		data->defAbility = GetMonAbility(monDef);
		data->defPartnerAbility = ABILITY_NONE;
		data->defItemEffect = GetMonItemEffect(monDef);
		data->defItemQuality = ItemId_GetHoldEffectParam(GetMonData(monDef, MON_DATA_HELD_ITEM));
		data->defHP = monDef->hp;
		data->defMaxHP = monDef->maxHP;
		data->defSpeed = SpeedCalcMon(SIDE(bankDef), monDef);
		data->defStatus1 = GetMonData(monDef, MON_DATA_STATUS);
		data->defStatus3 = 0;
		data->defSideStatus = gSideStatuses[SIDE(bankDef)];
		data->defIsGrounded = CheckGroundingFromPartyData(monDef);

		data->defBuff = 0;
		data->spDefBuff = 0;

		if (IsWonderRoomActive())
		{
			data->defense = monDef->spDefense;
			data->spDefense = monDef->defense;
		}
		else
		{
			data->defense = monDef->defense;
			data->spDefense = monDef->spDefense;
		}
	}
	else //Load from bank
	{
		data->defSpecies = SPECIES(bankDef);
		data->defAbility = ABILITY(bankDef);
		if (IS_DOUBLE_BATTLE && BATTLER_ALIVE(PARTNER(bankDef)))
			data->defPartnerAbility = ABILITY(PARTNER(bankDef));
		else
			data->defPartnerAbility = 0;
		data->defItemEffect = ITEM_EFFECT(bankDef);
		data->defItemQuality = ITEM_QUALITY(bankDef);
		data->defHP = gBattleMons[bankDef].hp;
		data->defMaxHP = gBattleMons[bankDef].maxHP;
		data->defSpeed = SpeedCalc(bankDef);
		data->defStatus1 = gBattleMons[bankDef].status1;
		data->defStatus3 = gStatuses3[bankDef];
		data->defSideStatus = gSideStatuses[SIDE(bankDef)];
		data->defIsGrounded = CheckGrounding(bankDef);

		data->defBuff = STAT_STAGE(bankDef, STAT_DEF);
		data->spDefBuff = STAT_STAGE(bankDef, STAT_SPDEF);

		if (IsWonderRoomActive())
		{
			data->defense = gBattleMons[bankDef].spDefense;
			data->spDefense = gBattleMons[bankDef].defense;
		}
		else
		{
			data->defense = gBattleMons[bankDef].defense;
			data->spDefense = gBattleMons[bankDef].spDefense;
		}
	}

	data->defenderLoaded = TRUE;
}

bool8 CheckContact(u16 move, u8 bank)
{
	if (!(gBattleMoves[move].flags & FLAG_MAKES_CONTACT)
	|| ITEM_EFFECT(bank) == HOLD_EFFECT_PROTECTIVE_PADS
	|| ABILITY(bank) == ABILITY_LONG_REACH)
		return FALSE;

	return TRUE;
}

static u32 AccuracyCalcPassDefAbilityItemEffect(u16 move, u8 battlerAtk, u8 battlerDef, u16 defAbility, u16 defHoldEffect)
{
    u32 calc, moveAcc, atkHoldEffect, atkParam, defParam, atkAbility;
    s8 buff, accStage, evasionStage;

    atkAbility = GetBattlerAbility(battlerAtk);
    atkHoldEffect = GetBattlerHoldEffect(battlerAtk, TRUE);
    atkParam = GetBattlerHoldEffectParam(battlerAtk);

    defAbility = GetBattlerAbility(battlerDef);
    defParam = GetBattlerHoldEffectParam(battlerDef);
    gPotentialItemEffectBattler = battlerDef;

    accStage = gBattleMons[battlerAtk].statStages[STAT_ACC];
    evasionStage = gBattleMons[battlerDef].statStages[STAT_EVASION];
    if (atkAbility == ABILITY_UNAWARE)
        evasionStage = 6;
    if (gBattleMoves[move].flags & FLAG_STAT_STAGES_IGNORED)
        evasionStage = 6;
    if (defAbility == ABILITY_UNAWARE)
        accStage = 6;

    if (gBattleMons[battlerDef].status2 & STATUS2_FORESIGHT || gStatuses3[battlerDef] & STATUS3_MIRACLE_EYED)
        buff = accStage;
    else
        buff = accStage + 6 - evasionStage;

    if (buff < 0)
        buff = 0;
    if (buff > 0xC)
        buff = 0xC;

    moveAcc = gBattleMoves[move].accuracy;
    // Check Thunder and Hurricane on sunny weather.
    if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY
        && (gBattleMoves[move].effect == EFFECT_THUNDER || gBattleMoves[move].effect == EFFECT_HURRICANE))
        moveAcc = 50;
    // Check Wonder Skin.
    if (defAbility == ABILITY_WONDER_SKIN && gBattleMoves[move].power == 0)
        moveAcc = 50;

    calc = CalcAccRatio(moveAcc, buff);

    if (atkAbility == ABILITY_COMPOUND_EYES)
        calc = (calc * 130) / 100; // 1.3 compound eyes boost
    else if (atkAbility == ABILITY_VICTORY_STAR)
        calc = (calc * 110) / 100; // 1.1 victory star boost
    if (IsBattlerAlive(BATTLE_PARTNER(battlerAtk)) && GetBattlerAbility(BATTLE_PARTNER(battlerAtk)) == ABILITY_VICTORY_STAR)
        calc = (calc * 110) / 100; // 1.1 ally's victory star boost

    if (defAbility == ABILITY_SAND_VEIL && WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SANDSTORM_ANY)
        calc = (calc * 80) / 100; // 1.2 sand veil loss
    else if (defAbility == ABILITY_SNOW_CLOAK && WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_HAIL_ANY)
        calc = (calc * 80) / 100; // 1.2 snow cloak loss
    else if (defAbility == ABILITY_TANGLED_FEET && gBattleMons[battlerDef].status2 & STATUS2_CONFUSION)
        calc = (calc * 50) / 100; // 1.5 tangled feet loss

    if (atkAbility == ABILITY_HUSTLE && IS_MOVE_PHYSICAL(move))
        calc = (calc * 80) / 100; // 1.2 hustle loss

    if (defHoldEffect == HOLD_EFFECT_EVASION_UP)
        calc = (calc * (100 - defParam)) / 100;

    if (atkHoldEffect == HOLD_EFFECT_WIDE_LENS)
        calc = (calc * (100 + atkParam)) / 100;
    else if (atkHoldEffect == HOLD_EFFECT_ZOOM_LENS && GetBattlerTurnOrderNum(battlerAtk) > GetBattlerTurnOrderNum(battlerDef))
        calc = (calc * (100 + atkParam)) / 100;

    return calc;
}

u32 AccuracyCalc(u16 move, u8 bankAtk, u8 bankDef)
{
	return AccuracyCalcPassDefAbilityItemEffect(move, bankAtk, bankDef, ABILITY(bankDef), ITEM_EFFECT(bankDef));
}

u8 CountAliveMonsInBattle2(u8 caseId, u8 bankAtk, u8 bankDef)
{
	s32 i;
	u8 retVal = 0;

	switch (caseId)
	{
	case BATTLE_ALIVE_EXCEPT_ACTIVE:
		for (i = 0; i < gBattlersCount; ++i)
		{
			if (i != bankAtk && !(gAbsentBattlerFlags & gBitTable[i]) && BATTLER_ALIVE(i))
				retVal++;
		}
		break;
	case BATTLE_ALIVE_ATK_SIDE:
		for (i = 0; i < gBattlersCount; ++i)
		{
			if (SIDE(i) == SIDE(bankAtk) && !(gAbsentBattlerFlags & gBitTable[i]) && BATTLER_ALIVE(i))
				retVal++;
		}
		break;
	case BATTLE_ALIVE_DEF_SIDE:
		for (i = 0; i < gBattlersCount; ++i)
		{
			if (SIDE(i) == SIDE(bankDef) && !(gAbsentBattlerFlags & gBitTable[i]) && BATTLER_ALIVE(i))
				retVal++;
		}
		break;
	}

	return retVal;
}

bool8 IsAnyMaxMove(u16 move)
{
	return move >= MOVE_MAX_GUARD && move <= MOVE_HYDRO_VORTEX;
}

bool8 IsConfused(u8 bank)
{
	return (gBattleMons[bank].status2 & STATUS2_CONFUSION) != 0;
//		|| (IS_BATTLE_CIRCUS && gBattleCircusFlags & BATTLE_CIRCUS_CONFUSED && ABILITY(bank) != ABILITY_OWNTEMPO);
}

bool8 CanBeConfused(u8 bank, u8 checkSafeguard)
{
	if (IsConfused(bank))
		return FALSE;

	if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN && CheckGrounding(bank))
		return FALSE;

	if (ABILITY(bank) == ABILITY_OWN_TEMPO)
		return FALSE;

	if (checkSafeguard && gSideStatuses[SIDE(bank)] & SIDE_STATUS_SAFEGUARD && !(gHitMarker & HITMARKER_IGNORE_SAFEGUARD))
		return FALSE;

	return TRUE;
}

bool8 CheckTableForMove(u16 move,const u16 *table)
{
    u32 i;
    for (i = 0; table[i] != MOVE_TABLES_TERMIN; ++i)
	{
		if (move == table[i])
			return TRUE;
	}

	return FALSE;
}

u8 CheckMoveLimitationsFromParty(struct Pokemon* mon, u8 unusableMoves, u8 check)
{
	u8 holdEffect = GetMonItemEffect(mon);
    int i;
	for (i = 0; i < MAX_MON_MOVES; ++i)
	{
		u16 move = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);

		if (move == MOVE_NONE && check & MOVE_LIMITATION_ZEROMOVE)
			unusableMoves |= gBitTable[i];
		else if (GetMonData(mon, MON_DATA_PP1 + i, NULL) == 0 && check & MOVE_LIMITATION_PP)
			unusableMoves |= gBitTable[i];
		else if (holdEffect == HOLD_EFFECT_ASSAULT_VEST && SPLIT(move) == SPLIT_STATUS)
			unusableMoves |= gBitTable[i];
		#ifdef FLAG_SKY_BATTLE
		else if (FlagGet(FLAG_SKY_BATTLE) && CheckTableForMove(move, gSkyBattleBannedMoves))
			unusableMoves |= gBitTable[i];
		#endif
		else if (IsGravityPreventingMove(move))
			unusableMoves |= gBitTable[i];
		/*else if (IsRaidBattle() && CheckTableForMove(move, gRaidBattleBannedMoves))
			unusableMoves |= gBitTable[i];*/
	}

	return unusableMoves;
}

u8 TryRandomizeAbility(u8 ability, u16 species)
{
	u32 newAbility = ability;

	#ifdef FLAG_ABILITY_RANDOMIZER
	if (FlagGet(FLAG_ABILITY_RANDOMIZER) && !FlagGet(FLAG_BATTLE_FACILITY))
	{
		u32 id = MathMax(1, T1_READ_32(gSaveBlock2->playerTrainerId)); //0 id would mean Pokemon wouldn't have ability

		do
		{
			newAbility = newAbility * id * species;
			newAbility = MathMax(1, newAbility % ABILITIES_COUNT);
		}
		while (CheckTableForAbility(newAbility, gRandomizerAbilityBanList));
	}
	#endif

	return newAbility;
}

bool8 IsAbilitySuppressed(u8 bank)
{
	return (gStatuses3[bank] & STATUS3_GASTRO_ACID) != 0;
//		|| (IS_BATTLE_CIRCUS && gBattleCircusFlags & BATTLE_CIRCUS_ABILITY_SUPPRESSION);
}

u16 GetBankMegaFormAbility(u8 bank)
{
	u16 species = GetMegaEvolutionSpecies(gBattleMons[bank].species, gBattleMons[bank].item);

	if (!IsAbilitySuppressed(bank))
	{
		if (CanMegaEvolve(bank))
		{
			return TryRandomizeAbility(gBaseStats[species].abilities[0], species); //Megas can only have 1 ability
		}

//		evos = CanMegaEvolve(bank);
//		if (evos != NULL)
//		{
//			return TryRandomizeAbility(gBaseStats[evos->targetSpecies].abilities[1], evos->targetSpecies); //Ultra Necrozma only has 1 ability
//		}
	}

	return ABILITY_NONE;
}


u16 IsValidMovePrediction(u8 bankAtk, u8 bankDef)
{
	if (gBattleStruct->ai.movePredictions[bankAtk][bankDef] == MOVE_PREDICTION_SWITCH)
		return MOVE_NONE;
	else
		return gBattleStruct->ai.movePredictions[bankAtk][bankDef];
}


u8 GetMoveTypeSpecial(u8 bankAtk, u16 move)
{
    u16 moveType;
    SetTypeBeforeUsingMove(move, bankAtk);
    GET_MOVE_TYPE(move, moveType);
    return moveType;
}

u8 AttacksThisTurn(u8 bank, u16 move) // Note: returns 1 if it's a charging turn, otherwise 2
{
	u8 moveEffect = gBattleMoves[move].effect;

	// first argument is unused
	if (ITEM_EFFECT(bank) == HOLD_EFFECT_POWER_HERB)
		return 2;

	if (moveEffect == EFFECT_SOLARBEAM && (gBattleWeather & WEATHER_SUN_ANY) && WEATHER_HAS_EFFECT)
		return 2;

	if (moveEffect == EFFECT_SKULL_BASH
	||  moveEffect == EFFECT_TWO_TURNS_ATTACK
//	||  moveEffect == EFFECT_SKY_ATTACK
	||  moveEffect == EFFECT_SOLARBEAM
	||  moveEffect == EFFECT_SEMI_INVULNERABLE
	||  moveEffect == EFFECT_BIDE
	||	move == MOVE_GEOMANCY
	/*||  move == MOVE_SKYDROP*/)
	{
		if (gBattleMons[bank].status2 & STATUS2_MULTIPLETURNS)
			return 2;
		else
			return 1; //About to initiate attack.
	}

	return 2;
}


void TypeDamageModificationByDefTypes(u16 atkAbility, u8 defBattler, u16 move, u8 type, u8* flag, u8 type1, u8 type2, u8 type3)
{
    struct BattlePokemon* tempMon = Alloc(sizeof (struct BattlePokemon) * 2);
    tempMon[0] = gBattleMons[0];
    tempMon[1] = gBattleMons[defBattler];
    gBattleMons[0].ability = atkAbility;
    gBattleMons[defBattler].type1=type1;
    gBattleMons[defBattler].type2=type2;
    gBattleMons[defBattler].type3=type3;
    *flag = TypeCalc(move, 0, defBattler, NULL, FALSE);
    gBattleMons[0] = tempMon[0];
    gBattleMons[defBattler] = tempMon[1];
    Free(tempMon);
}

u8 AI_SpecialTypeCalc(u16 move, u8 bankAtk, u8 bankDef)
{
	u8 moveType;
	u16 atkAbility = GetAIAbility(bankAtk, bankDef, move);
	u16 defAbility = GetAIAbility(bankDef, bankAtk, IsValidMovePrediction(bankDef, bankAtk));
	u8 defEffect = ITEM_EFFECT(bankDef);
	u8 atkType1, atkType2, atkType3, defType1, defType2, defType3;
	u8 flags = 0;

	if (move == MOVE_STRUGGLE)
		return 0;

	atkType1 = gBattleMons[bankAtk].type1;
	atkType2 = gBattleMons[bankAtk].type2;
	atkType3 = gBattleMons[bankAtk].type3;
	moveType = GetMoveTypeSpecial(bankAtk, move);

	if (atkAbility == ABILITY_PROTEAN)
		atkType1 = atkType2 = atkType3 = moveType;

	if (gBattleStruct->illusion[bankDef].on && gDisableStructs[bankDef].isFirstTurn) //Under illusion and haven't figured it out yet
	{
		struct Pokemon* illusionMon = GetIllusionMonPtr(bankDef);
		u16 fakeSpecies = GetMonData(illusionMon, MON_DATA_SPECIES, NULL);
		defAbility = GetMonAbility(illusionMon);
		defType1 = gBaseStats[fakeSpecies].type1;
		defType2 = gBaseStats[fakeSpecies].type2;
	}
	else
	{
		defType1 = gBattleMons[bankDef].type1;
		defType2 = gBattleMons[bankDef].type2;
	}
	defType3 = gBattleMons[bankDef].type3; //Same type 3 - eg switched in on Forest's curse

	//Check STAB
	if (atkType1 == moveType || atkType2 == moveType || atkType3 == moveType)
	{
		if (atkAbility == ABILITY_ADAPTABILITY)
			gBattleMoveDamage *= 2;
		else
			gBattleMoveDamage = (gBattleMoveDamage * 150) / 100;
	}

	//Check Special Ground Immunities
	if (moveType == TYPE_GROUND
	&& !CheckGrounding(bankDef)
	&& ((defAbility == ABILITY_LEVITATE && NO_MOLD_BREAKERS(atkAbility, move)) || defEffect == HOLD_EFFECT_AIR_BALLOON || (gStatuses3[bankDef] & (/*STATUS3_MAGNET_RISE | */STATUS3_TELEKINESIS)))
	&& move != MOVE_THOUSAND_ARROWS)
	{
		flags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE);
	}
	else if (CheckTableForMove(move, gPowderMoves)/* && !IsAffectedByPowderByDetails(defType1, defType2, defType3, defAbility, defEffect)*/)
	{
		flags |= (MOVE_RESULT_MISSED | MOVE_RESULT_DOESNT_AFFECT_FOE);
	}
	else if (move == MOVE_SKY_DROP && (defType1 == TYPE_FLYING || defType2 == TYPE_FLYING || defType3 == TYPE_FLYING))
	{
		flags |= (MOVE_RESULT_DOESNT_AFFECT_FOE);
	}
	else //Regular Type Calc
		TypeDamageModificationByDefTypes(atkAbility, bankDef, move, moveType, &flags, defType1, defType2, defType3);

	//Wonder Guard Check
	if (defAbility == ABILITY_WONDER_GUARD
	&& NO_MOLD_BREAKERS(atkAbility, move)
	&& !(flags & MOVE_RESULT_MISSED)
	&& AttacksThisTurn(bankAtk, move) == 2
	&& (!(flags & MOVE_RESULT_SUPER_EFFECTIVE) || ((flags & (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)) == (MOVE_RESULT_SUPER_EFFECTIVE | MOVE_RESULT_NOT_VERY_EFFECTIVE)))
	&& gBattleMoves[move].power
	&& SPLIT(move) != SPLIT_STATUS)
	{
		flags |= MOVE_RESULT_MISSED;
	}

	if (flags & MOVE_RESULT_NO_EFFECT)
		gBattleMoveDamage = 0;

	return flags;
}

bool8 IsOfType(u32 battler, u8 type)
{
    return BattlerHasType(battler, type);
}

bool8 CheckSoundMove(u16 move)
{
	return CheckTableForMove(move, gSoundMoves);
}

bool8 MoveIgnoresSubstitutes(u16 move, u8 atkAbility)
{
	return CheckSoundMove(move)
		|| (atkAbility == ABILITY_INFILTRATOR && move != MOVE_TRANSFORM && move != MOVE_SKY_DROP)
		|| CheckTableForMove(move, gSubstituteBypassMoves);
}

bool8 MoveBlockedBySubstitute(u16 move, u8 bankAtk, u8 bankDef)
{
	return IS_BEHIND_SUBSTITUTE(bankDef) && !MoveIgnoresSubstitutes(move, ABILITY(bankAtk));
}

u8 FindMovePositionInMoveset(u16 move, u8 bank)
{
	int i;

	for (i = 0; i < MAX_MON_MOVES; ++i)
	{
		if (gBattleMons[bank].moves[i] == move)
			break;
	}

	return i;
}

bool8 MonMoveBlockedBySubstitute(u16 move, struct Pokemon* monAtk, u8 bankDef)
{
	return IS_BEHIND_SUBSTITUTE(bankDef) && !MoveIgnoresSubstitutes(move, GetMonAbility(monAtk));
}

u32 AI_CalcDmg(const u8 bankAtk, const u8 bankDef, const u16 move, struct DamageCalc* damageData)
{
	return AI_CalcDamage(move, bankAtk, bankDef);
}

u32 AI_CalcPartyDmg(u8 bankAtk, u8 bankDef, u16 move, struct Pokemon* mon, struct DamageCalc* damageData)
{
    return AI_CalcPartyMonDamage(move, bankAtk, bankDef, mon);
}

//这个方法应该本来是上面方法的简化版，以节约性能用的。
u32 AI_CalcMonDefDmg(u8 bankAtk, u8 bankDef, u16 move, struct Pokemon* monDef, struct DamageCalc* damageData)
{
    return AI_CalcPartyMonDamage(move, bankAtk, bankDef, monDef);
}

u8 CalcMoveSplit(u8 bank, u16 move)
{
    return gBattleMoves[move].split;
}

s32 BracketCalc(u8 bank)
{
    u16 itemEffect = ITEM_EFFECT(bank);
    u8 itemQuality = ITEM_QUALITY(bank);
    u16 ability = ABILITY(bank);

//    gNewBS->CustapQuickClawIndicator &= ~(gBitTable[bank]); //Reset the Quick Claw counter just in case
    if (BATTLER_ALIVE(bank))
    {
        switch (itemEffect) {
            case HOLD_EFFECT_QUICK_CLAW:
                if (gRandomTurnNumber % 100 < itemQuality)
                {
//                    gNewBS->CustapQuickClawIndicator |= gBitTable[bank];
                    return 1;
                }
                break;

           /* case HOLD_EFFECT_CUSTAP_BERRY:
                if (!AbilityBattleEffects(ABILITYEFFECT_CHECK_OTHER_SIDE, bank, ABILITY_UNNERVE, 0, 0)
                    && PINCH_BERRY_CHECK(bank))
                {
                    gNewBS->CustapQuickClawIndicator |= gBitTable[bank];
                    return 1;
                }
                break;*/

            case HOLD_EFFECT_LAGGING_TAIL:
                return -2;
        }

        if (ability == ABILITY_STALL)
            return -1;
    }

    return 0;
}

bool8 IsTrickRoomActive(void)
{
    return gFieldStatuses & STATUS_FIELD_TRICK_ROOM;
}

bool8 IsDynamaxed(u8 battler)
{
    return CurrentBttlerEvolutionStat(battler) == EvolutionDynamax;
}

bool8 MoveInMoveset(u16 move, u8 bank)
{
    return FindMovePositionInMoveset(move, bank) < MAX_MON_MOVES;
}

u16 GetBaseMaxHP(u8 bank)
{
//    if (IsDynamaxed(bank))
//    {
//        //Ceiling
//        return max(1, gBattleMons[bank].maxHP / GetDynamaxHPBoost(bank) + (gBattleMons[bank].maxHP & 1));
//    }

    return gBattleMons[bank].maxHP;
}

u16 GetBaseCurrentHP(u8 bank)
{
//    if (IsDynamaxed(bank))
//    {
//        //Ceiling
//        return max(1, gBattleMons[bank].hp / GetDynamaxHPBoost(bank) + (gBattleMons[bank].hp & 1));
//    }

    return gBattleMons[bank].hp;
}

bool8 TakesGeneralWeatherDamage(u8 bank)
{
    u16 ability = ABILITY(bank);
    u16 effect = ITEM_EFFECT(bank);

    return WEATHER_HAS_EFFECT
           && 	ability != ABILITY_MAGIC_GUARD
           &&	ability != ABILITY_OVERCOAT
           &&	effect  != HOLD_EFFECT_SAFETY_GOOGLES
           && !(gStatuses3[bank] & (STATUS3_UNDERGROUND | STATUS3_UNDERWATER)); //For some strange reason, Pokemon using Shadow/Phantom Force still take weather damage
}


bool8 SandstormHurts(u8 bank)
{
    u16 ability = ABILITY(bank);

    if (TakesGeneralWeatherDamage(bank))
    {
        if (!IsOfType(bank, TYPE_ROCK) && !IsOfType(bank, TYPE_GROUND) && !IsOfType(bank, TYPE_STEEL)
            && ability != ABILITY_SAND_VEIL && ability != ABILITY_SAND_RUSH && ability != ABILITY_SAND_FORCE)
            return TRUE;
    }

    return FALSE;
}

bool8 TakesDamageFromSandstorm(u8 bank)
{
    if (WEATHER_HAS_EFFECT
        &&  gBattleWeather & WEATHER_SANDSTORM_ANY)
    {
        return SandstormHurts(bank);
    }

    return FALSE;
}

bool8 HailHurts(u8 bank)
{
    u16 ability = ABILITY(bank);

    if (TakesGeneralWeatherDamage(bank))
    {
        if (!IsOfType(bank, TYPE_ICE) && ability != ABILITY_ICE_BODY && ability != ABILITY_SNOW_CLOAK && ability != ABILITY_SLUSH_RUSH)
            return TRUE;
    }

    return FALSE;
}

bool8 TakesDamageFromHail(u8 bank)
{
    if (WEATHER_HAS_EFFECT
        &&  gBattleWeather & WEATHER_HAIL_ANY)
    {
        return HailHurts(bank);
    }

    return FALSE;
}

s32 GetStealthHazardDamageMon(u8 hazardType, struct Pokemon* mon)
{
    u32 maxHp = GetMonData(mon, MON_DATA_MAX_HP);
    u16 modifier = UQ_4_12(1.0);
    u8 type1 = GetMonType(mon, BATTLE_MON_TYPE_1);
    u8 type2 = GetMonType(mon, BATTLE_MON_TYPE_2);
    MulModifier(&modifier, GetTypeModifier(hazardType, type1));
    if (type2 != type1)
        MulModifier(&modifier, GetTypeModifier(hazardType, type2));

    return CalcStealthHazardDamage(maxHp, modifier);
}

u32 CalcStealthRockDamagePartyMon(struct Pokemon* mon)
{
    u8 flags;
    u8 divisor = 8;
    gBattleMoveDamage = 40;

    if (GetMonItemEffect(mon) == HOLD_EFFECT_HEAVY_DUTY_BOOTS)
        return 0;

    return GetStealthHazardDamageMon(TYPE_ROCK, mon);
}

u32 CalcSpikesDamagePartyMon(struct Pokemon* mon, u8 side)
{
    u32 dmg;
    if (GetMonItemEffect(mon) == HOLD_EFFECT_HEAVY_DUTY_BOOTS)
        return 0;

    dmg = (5 - gSideTimers[side].spikesAmount) * 2;
    return max(1, GetMonData(mon, MON_DATA_MAX_HP, NULL) / dmg);
}

struct Pokemon* LoadPartyRange(u8 bank, u8* firstMonId, u8* lastMonId)
{
    struct Pokemon* party;

    party = (SIDE(bank) == B_SIDE_OPPONENT) ? gEnemyParty : gPlayerParty;

    if (gBattleTypeFlags & BATTLE_TYPE_INGAME_PARTNER && SIDE(bank) == B_SIDE_PLAYER)
    {
        *firstMonId = 0;
        if (GetBattlerPosition(bank) == B_POSITION_PLAYER_RIGHT)
            *firstMonId = 3;

        *lastMonId = *firstMonId + 3;
    }
    else if (gBattleTypeFlags & BATTLE_TYPE_MULTI)
    {
        //Two Human Trainers vs Two AI Trainers
        /*if (gBattleTypeFlags & BATTLE_TYPE_TOWER_LINK_MULTI)
        {
            if (SIDE(bank) == B_SIDE_PLAYER)
            {
                *firstMonId = 0;
                if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(bank)) == TRUE)
                    *firstMonId = 3;
            }
            else //B_SIDE_OPPONENT
            {
                if (bank == B_POSITION_OPPONENT_LEFT)
                    *firstMonId = 0;
                else
                    *firstMonId = 3;
            }
        }
            //Two Human Trainers vs Two Human Trainers
        else*/
        {
            *firstMonId = 0;
            if (GetLinkTrainerFlankId(GetBattlerMultiplayerId(bank)) == TRUE)
                *firstMonId = 3;
        }

        *lastMonId = *firstMonId + 3;
    }
    else if (gBattleTypeFlags & BATTLE_TYPE_TWO_OPPONENTS && SIDE(bank) == B_SIDE_OPPONENT)
    {
        *firstMonId = 0;
        if (GetBattlerPosition(bank) == B_POSITION_OPPONENT_RIGHT)
            *firstMonId = 3;

        *lastMonId = *firstMonId + 3;
    }
    else //Single Battle // Regular Double Battle // Regular Double Against Two Trainers + PlayerSide // Wild Double Battle
    {
        *firstMonId = 0;
        *lastMonId = PARTY_SIZE;
    }

    return party;
}

u16 GetMaxMoveByMove(u32 battlerId, u16 move)
{
    const struct Evolution *evolution = FindBattlerEvolution(gBattleMons[battlerId].species, EVO_DYNAMAX);
    return CalcMaxMove(evolution, move);
}

u16 GetMaxMove(u32 battlerId, u8 index)
{
   return GetMaxMoveByMove(battlerId, gBattleMons[battlerId].moves[index]);
}

bool8 ToEvolution(u32 battlerId)
{
    return gBattleStruct->mega.toEvolve & (1 << battlerId);
}

bool8 ToUseZMove(u32 battlerId)
{
    return ToEvolution(battlerId) && GetEvolutionType(battlerId) == EvolutionZmove;
}

bool8 ToDyanamx(u32 battlerId)
{
    return ToEvolution(battlerId) && GetEvolutionType(battlerId) == EvolutionDynamax;
}


bool8 UsedZMove(u32 battler)
{
    return IsEvolutionHappened(battler, EvolutionZmove);
}

bool8 IsBattlerDynamaxEnd(u32 battler)
{
    return IsEvolutionHappened(battler, EvolutionDynamax);
}

u16 GetZMoveByMove(u32 bankAtk, u16 move)
{
    return ConvertZMove(move, gBattleMons[bankAtk].species, gBattleMons[bankAtk].item);
}

u16 ReplaceWithZMoveRuntime(u8 bankAtk, u16 move)
{
    if (ToUseZMove(bankAtk)
        && SPLIT(move) != SPLIT_STATUS)
    {
        u16 zMove = GetZMoveByMove(bankAtk, move);
        if (zMove != MOVE_NONE)
            move = zMove;
    }
    else if (IsDynamaxed(bankAtk) || ToDyanamx(bankAtk))
    {
       /* if (IsRaidBattle() && bankAtk == BANK_RAID_BOSS && IsRaidBossUsingRegularMove(bankAtk, move))
            return move; //This turn the raid boss isn't using a Max Move*/

        u16 maxMove = GetMaxMoveByMove(bankAtk, move);
        if (maxMove != MOVE_NONE)
            move = maxMove;
    }

    return move;
}

bool8 IsFairyLockActive(void)
{
    return gFieldTimers.fairyLockTimer > 0;
//           || (IS_BATTLE_CIRCUS && gBattleCircusFlags & BATTLE_CIRCUS_FAIRY_LOCK);
}

u8 ViableMonCount(struct Pokemon* party)
{
    u8 count = 0;
    u32 i;
    for (i = 0; i < PARTY_SIZE; ++i)
    {
        if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE
            && !GetMonData(&party[i], MON_DATA_IS_EGG)
            &&  GetMonData(&party[i], MON_DATA_HP) > 0)
            ++count;
    }

    return count;
}

u8 ViableMonCountFromBank(u8 bank)
{
    return (SIDE(bank) == B_SIDE_PLAYER) ? ViableMonCount(gPlayerParty) : ViableMonCount(gEnemyParty);
}

bool8 IsTagBattle(void)
{
    const u32 flags = (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_INGAME_PARTNER);

    if ((gBattleTypeFlags & flags) == flags)
        return TRUE;
    else
        return FALSE;
}

u8 ViableMonCountFromBankLoadPartyRange(u8 bank)
{
    u8 count = 0;
    u8 firstMonId, lastMonId;
    struct Pokemon* party = LoadPartyRange(bank, &firstMonId, &lastMonId);
    int i;
    for (i = firstMonId; i < lastMonId; ++i)
    {
        if (GetMonData(&party[i], MON_DATA_SPECIES) != SPECIES_NONE
            && !GetMonData(&party[i], MON_DATA_IS_EGG)
            && party[i].hp != 0)
            ++count;
    }

    return count;
}

bool8 IsTwoOpponentBattle(void)
{
    if ((gBattleTypeFlags & (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS)) == (BATTLE_TYPE_DOUBLE | BATTLE_TYPE_TWO_OPPONENTS))
        return TRUE;

    return FALSE;
}

bool8 BankSideHasSeaOfFire(u8 bank)
{
    return FALSE;//gSideTimers[SIDE(bank)].;
}

bool8 BankSideHasGMaxVineLash(u8 bank)
{
    return FALSE;
}

bool8 BankSideHasGMaxWildfire(u8 bank)
{
    return FALSE;
}

bool8 BankSideHasGMaxCannonade(u8 bank)
{
    return FALSE;
}

bool8 BankSideHasGMaxVolcalith(u8 bank)
{
    return FALSE;
}

u32 GetSandstormDamage(u8 bank)
{
    u32 damage = 0;

    if (TakesDamageFromSandstorm(bank))
        damage = max(1, GetBaseMaxHP(bank) / 16);

    return damage;
}

u32 GetHailDamage(u8 bank)
{
    u32 damage = 0;

    if (TakesDamageFromHail(bank))
        damage = max(1, GetBaseMaxHP(bank) / 16);

    return damage;
}

u32 GetNightmareDamage(u8 bank)
{
    u32 damage = 0;

    if (gBattleMons[bank].status2 & STATUS2_NIGHTMARE
        && gBattleMons[bank].status1 & STATUS1_SLEEP
        && ABILITY(bank) != ABILITY_MAGIC_GUARD)
    {
        damage = max(1, GetBaseMaxHP(bank) / 4);
    }

    return damage;
}

u32 GetTrapDamage(u8 bank)
{
    u32 damage = 0;

    if (gBattleMons[bank].status2 & STATUS2_WRAPPED
        && ABILITY(bank) != ABILITY_MAGIC_GUARD)
    {
        if (/*(gNewBS->sandblastCentiferno[gActiveBattler] & 2) //Trapped by this move and user held Binding Band
            || */ITEM_EFFECT(gBattleStruct->wrappedBy[bank]) == HOLD_EFFECT_BINDING_BAND)
            damage = max(1, GetBaseMaxHP(bank) / 6);
        else
            damage = max(1, GetBaseMaxHP(bank) / 8);
    }

    return damage;
}

u32 GetLeechSeedDamage(u8 bank)
{
    u32 damage = 0;

    if (gStatuses3[bank] & STATUS3_LEECHSEED
        && ABILITY(bank) != ABILITY_MAGIC_GUARD)
        damage = max(1, GetBaseMaxHP(bank) / 8);

    return damage;
}

u32 GetPoisonDamage(u8 bank)
{
    u32 damage = 0;
    u16 ability = ABILITY(bank);

    if (ability != ABILITY_MAGIC_GUARD
        && ability != ABILITY_POISON_HEAL)
    {
        if (gBattleMons[bank].status1 & STATUS1_POISON)
        {
            damage = max(1, GetBaseMaxHP(bank) / 8);
        }
        else if (gBattleMons[bank].status1 & STATUS1_TOXIC_POISON)
        {
            damage = max(1, GetBaseMaxHP(bank) / 16);
            damage *= (gBattleMons[gActiveBattler].status1 & 0xF00) >> 8;
        }
    }
    else if (ability == ABILITY_POISON_HEAL)
        damage = max(1, GetBaseMaxHP(bank) / 8);

    return damage;
}

u32 GetBurnDamage(u8 bank)
{
    u32 damage = 0;
    u16 ability = ABILITY(bank);

    if (gBattleMons[bank].status1 & STATUS1_BURN
        && ability != ABILITY_MAGIC_GUARD)
    {
        if (ability == ABILITY_HEATPROOF)
        {
#ifdef OLD_BURN_DAMAGE
            damage = MathMax(1, GetBaseMaxHP(bank) / 16);
#else
            damage = max(1, GetBaseMaxHP(bank) / 32);
#endif
        }
        else
        {
#ifdef OLD_BURN_DAMAGE
            damage = MathMax(1, GetBaseMaxHP(bank) / 8);
#else
            damage = max(1, GetBaseMaxHP(bank) / 16);
#endif
        }
    }

    return damage;
}

u32 GetCurseDamage(u8 bank)
{
    u32 damage = 0;

    if (gBattleMons[bank].status2 & STATUS2_CURSED
        && ABILITY(bank) != ABILITY_MAGIC_GUARD)
    {
        damage = max(1, GetBaseMaxHP(bank) / 4);
    }

    return damage;
}

u32 GetSeaOfFireDamage(u8 bank)
{
    u32 damage = 0;

    if (BankSideHasSeaOfFire(bank)
        && !IsOfType(bank, TYPE_FIRE)
        && ABILITY(bank) != ABILITY_MAGIC_GUARD)
    {
        damage = max(1, GetBaseMaxHP(bank) / 8);
    }

    return damage;
}

u32 GetGMaxVineLashDamage(u8 bank)
{
    u32 damage = 0;

    if (BankSideHasGMaxVineLash(bank)
        && !IsOfType(bank, TYPE_GRASS)
        && ABILITY(bank) != ABILITY_MAGIC_GUARD)
    {
        damage = max(1, GetBaseMaxHP(bank) / 6);
    }

    return damage;
}

u32 GetGMaxWildfireDamage(u8 bank)
{
    u32 damage = 0;

    if (BankSideHasGMaxWildfire(bank)
        && !IsOfType(bank, TYPE_FIRE)
        && ABILITY(bank) != ABILITY_MAGIC_GUARD)
    {
        damage = max(1, GetBaseMaxHP(bank) / 6);
    }

    return damage;
}

u32 GetGMaxCannonadeDamage(u8 bank)
{
    u32 damage = 0;

    if (BankSideHasGMaxCannonade(bank)
        && !IsOfType(bank, TYPE_WATER)
        && ABILITY(bank) != ABILITY_MAGIC_GUARD)
    {
        damage = max(1, GetBaseMaxHP(bank) / 6);
    }

    return damage;
}

u32 GetGMaxVolcalithDamage(u8 bank)
{
    u32 damage = 0;

    if (BankSideHasGMaxVolcalith(bank)
        && ABILITY(bank) != ABILITY_MAGIC_GUARD)
    {
        damage = max(1, GetBaseMaxHP(bank) / 6);
    }

    return damage;
}

bool8 BankSideHasRainbow(u8 bank)
{
    return FALSE;
}

bool8 CanBeGeneralStatused(u8 bank, bool8 checkFlowerVeil)
{
#ifdef SPECIES_MINIOR_SHIELD
    if (ABILITY(bank) == ABILITY_SHIELDSDOWN
	&&  GetBankPartyData(bank)->species == SPECIES_MINIOR_SHIELD) //Prevents Ditto from getting this benefit
		return FALSE;
#endif

    switch (ABILITY(bank)) {
        case ABILITY_COMATOSE:
            return FALSE;

        case ABILITY_LEAF_GUARD:
            if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY && ITEM_EFFECT(bank) != HOLD_EFFECT_UTILITY_UMBRELLA)
                return FALSE;
            break;

        case ABILITY_FLOWER_VEIL:
            if (checkFlowerVeil && IsOfType(bank, TYPE_GRASS))
                return FALSE;
    }

    if (checkFlowerVeil && ABILITY(PARTNER(bank)) == ABILITY_FLOWER_VEIL && IsOfType(bank, TYPE_GRASS) && !(gHitMarker & HITMARKER_IGNORE_SAFEGUARD))
        return FALSE;

    if ((gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN) && CheckGrounding(bank))
        return FALSE;

    if (gBattleMons[bank].status1 != STATUS1_NONE)
        return FALSE;

    if (checkFlowerVeil && gSideStatuses[SIDE(bank)] & SIDE_STATUS_SAFEGUARD && !(gHitMarker & HITMARKER_IGNORE_SAFEGUARD))
        return FALSE;

    return TRUE;
}

bool8 CanBePoisoned(u8 bankDef, u8 bankAtk, bool8 checkFlowerVeil)
{
    if (!CanBeGeneralStatused(bankDef, checkFlowerVeil))
        return FALSE;

    switch (ABILITY(bankDef)) {
        case ABILITY_IMMUNITY:
        case ABILITY_PASTEL_VEIL:
            return FALSE;
    }

    if (IS_DOUBLE_BATTLE && ABILITY(PARTNER(bankDef)) == ABILITY_PASTEL_VEIL)
        return FALSE;

    if (ABILITY(bankAtk) != ABILITY_CORROSION)
    {
        if (IsOfType(bankDef, TYPE_POISON) || IsOfType(bankDef, TYPE_STEEL))
            return FALSE;
    }

    return TRUE;
}

bool8 CanBeParalyzed(u8 bank, bool8 checkFlowerVeil)
{
    if (!CanBeGeneralStatused(bank, checkFlowerVeil))
        return FALSE;

    if (IsOfType(bank, TYPE_ELECTRIC))
        return FALSE;

    switch (ABILITY(bank)) {
        case ABILITY_LIMBER:
            return FALSE;
    }

    return TRUE;
}

bool8 CanBeBurned(u8 bank, bool8 checkFlowerVeil)
{
    if (!CanBeGeneralStatused(bank, checkFlowerVeil))
        return FALSE;

    if (IsOfType(bank, TYPE_FIRE))
        return FALSE;

    switch (ABILITY(bank)) {
        case ABILITY_WATER_VEIL:
        case ABILITY_WATER_BUBBLE:
            return FALSE;
    }

    return TRUE;
}

bool8 CanBeFrozen(u8 bank, bool8 checkFlowerVeil)
{
    if (!CanBeGeneralStatused(bank, checkFlowerVeil))
        return FALSE;

    if (IsOfType(bank, TYPE_ICE))
        return FALSE;

    switch (ABILITY(bank)) {
        case ABILITY_MAGMA_ARMOR:
            return FALSE;
    }

    if (WEATHER_HAS_EFFECT && gBattleWeather & WEATHER_SUN_ANY && ITEM_EFFECT(bank) != HOLD_EFFECT_UTILITY_UMBRELLA)
        return FALSE;

    return TRUE;
}

bool8 DoesSleepClausePrevent(u8 bank)
{
    /*if (gBattleTypeFlags & BATTLE_TYPE_FRONTIER)
    {
        switch (VarGet(VAR_BATTLE_FACILITY_TIER)) {
            case BATTLE_FACILITY_OU:
            case BATTLE_FACILITY_UBER:
            case BATTLE_FACILITY_LITTLE_CUP:
            case BATTLE_FACILITY_MONOTYPE:
            case BATTLE_FACILITY_CAMOMONS:
            case BATTLE_FACILITY_UBER_CAMOMONS:
            case BATTLE_FACILITY_LC_CAMOMONS:
            case BATTLE_FACILITY_SCALEMONS:
            case BATTLE_FACILITY_350_CUP:
            case BATTLE_FACILITY_AVERAGE_MONS:
            case BATTLE_FACILITY_BENJAMIN_BUTTERFREE:
            case BATTLE_FACILITY_NATIONAL_DEX_OU: ;
                u8 firstId, lastId;
                struct Pokemon* party = LoadPartyRange(bank, &firstId, &lastId);

                for (int i = 0; i < PARTY_SIZE; ++i)
                {
                    if (GetMonData(&party[i], MON_DATA_HP, NULL) != 0
                        && !GetMonData(&party[i], MON_DATA_IS_EGG, NULL)
                        && GetMonData(&party[i], MON_DATA_STATUS, NULL) & STATUS_SLEEP) //Someone on team is already asleep
                        return TRUE;
                }
        }
    }*/

    return FALSE;
}

bool8 CanBePutToSleep(u8 bank, bool8 checkFlowerVeil)
{
    if (!CanBeGeneralStatused(bank, checkFlowerVeil))
        return FALSE;

    switch (ABILITY(bank)) {
        case ABILITY_INSOMNIA:
        case ABILITY_VITAL_SPIRIT:
        case ABILITY_SWEET_VEIL:
            return FALSE;
    }

    if ((gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN) && CheckGrounding(bank))
        return FALSE;

    if (DoesSleepClausePrevent(bank))
        return FALSE;

    return TRUE;
}

bool8 DoesProtectionMoveBlockMove(u8 bankAtk, u8 bankDef, u16 atkMove, u16 protectMove)
{
    u8 protectFlag = gBattleMoves[atkMove].flags & FLAG_PROTECT_AFFECTED;
    u8 split = SPLIT(atkMove);
    u8 target = gBattleMoves[atkMove].target;

    if (!CheckTableForMove(atkMove, gMovesThatLiftProtectTable))
    {
        switch (protectMove) {
            case MOVE_PROTECT:
            case MOVE_SPIKY_SHIELD:
            case MOVE_BANEFUL_BUNKER:
                return protectFlag != 0;

            case MOVE_KINGS_SHIELD:
            case MOVE_OBSTRUCT:
                return protectFlag && split != SPLIT_STATUS;

            case MOVE_MAT_BLOCK:
                return gDisableStructs[bankDef].isFirstTurn && protectFlag && split != SPLIT_STATUS;

            case MOVE_CRAFTY_SHIELD:
                return target != MOVE_TARGET_USER && split == SPLIT_STATUS;

            case MOVE_QUICK_GUARD:
                return protectFlag && PriorityCalc(bankAtk, ACTION_USE_MOVE, atkMove) > 0;

            case MOVE_WIDE_GUARD:
                return protectFlag && target & (MOVE_TARGET_BOTH | MOVE_TARGET_FOES_AND_ALLY);
        }
    }

    return FALSE;
}

u8 CalcMoveSplitFromParty(struct Pokemon* mon, u16 move)
{
    if (CheckTableForMove(move, gMovesThatChangePhysicality))
    {
        if (mon->spAttack >= mon->attack)
            return SPLIT_SPECIAL;
        else
            return SPLIT_PHYSICAL;
    }

    return SPLIT(move);
}

bool8 CanDynamax(u8 bank)
{
    return CanMegaEvolve(bank) == EvolutionDynamax;
}

bool8 CanKnockOffItem(u8 bank)
{
    return CheckCanKnockOff(bank);
}

u16 TypeCalc(u16 move, u8 bankAtk, u8 bankDef, struct Pokemon* monAtk, bool8 checkParty)
{
    struct BattlePokemon tempMon;
    u16 modifier;
    u16 backUpResultFlag;
    if (checkParty) {
        tempMon = gBattleMons[bankAtk];
        PokemonToBattleMon(monAtk, &gBattleMons[bankAtk]);
    }
    modifier = AI_GetTypeEffectiveness( move,  bankAtk, bankDef);
    backUpResultFlag = gMoveResultFlags;
    UpdateMoveResultFlags(modifier);
    SWAP(backUpResultFlag, gMoveResultFlags, modifier);
    if (checkParty) {
        gBattleMons[bankAtk] = tempMon;
    }
    return backUpResultFlag;
}

bool8 CanBeInfatuated(u8 bankDef, u8 bankAtk)
{
    u32 speciesAtk, speciesDef;
    u32 pidAtk, pidDef;

    speciesAtk = gBattleMons[bankAtk].species;
    pidAtk = gBattleMons[bankAtk].personality;

    speciesDef = gBattleMons[bankDef].species;
    pidDef = gBattleMons[bankDef].personality;
    if (!(gMoveResultFlags & MOVE_RESULT_NO_EFFECT)
        && gBattleMons[bankAtk].hp != 0
        && TARGET_TURN_DAMAGED
        && gBattleMons[bankDef].hp != 0
        && GetBattlerAbility(bankAtk) != ABILITY_OBLIVIOUS
        && !IsAbilityOnSide(bankAtk, ABILITY_AROMA_VEIL)
        && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != GetGenderFromSpeciesAndPersonality(speciesDef, pidDef)
        && !(gBattleMons[bankAtk].status2 & STATUS2_INFATUATION)
        && GetGenderFromSpeciesAndPersonality(speciesAtk, pidAtk) != MON_GENDERLESS
        && GetGenderFromSpeciesAndPersonality(speciesDef, pidDef) != MON_GENDERLESS)
        return TRUE;
    return FALSE;
}

bool8 IsBerry(u16 item)
{
    return GetPocketByItemId(item) == BERRIES_POCKET;
}

bool8 StatsMaxed(u8 bank)
{
    u8 i;
    for (i = STAT_ATK; i < NUM_NATURE_STATS; ++i)
    {
        if (STAT_STAGE(bank, i) < MAX_STAT_STAGE)
            return FALSE;
    }

    return TRUE;
}

u16 GetMonMaxMoveByMove(struct Pokemon* mon, u16 move)
{
    const struct Evolution *evolution = FindBattlerEvolution(GetMonData(mon, MON_DATA_SPECIES), EVO_DYNAMAX);
    return CalcMaxMove(evolution, move);
}


bool32 MonCanDynamax(struct Pokemon* mon)
{
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    u16 item = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
    u16 itemEffect = ItemId_GetHoldEffect(item);
    if (itemEffect == HOLD_EFFECT_Z_CRYSTAL || itemEffect == HOLD_EFFECT_MEGA_STONE)
        return FALSE;
    if (CanEvolve(species))
        return FALSE;
    if (FindBattlerEvolution(species, EVO_CANNOT_DYNAMAX) != 0)
        return FALSE;
    return TRUE;
}

bool8 MonCanUseMaxMoveWithEffect(struct Pokemon* mon, u8 maxEffect)
{
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    u16 item = GetMonData(mon, MON_DATA_HELD_ITEM, NULL);
    u16 itemEffect = ItemId_GetHoldEffect(item);
    u32 i;
    if (itemEffect == HOLD_EFFECT_Z_CRYSTAL || itemEffect == HOLD_EFFECT_MEGA_STONE)
        return FALSE;
    if (CanEvolve(species))
        return FALSE;
    if (FindBattlerEvolution(species, EVO_CANNOT_DYNAMAX) != 0)
        return FALSE;

    for (i = 0; i < MAX_MON_MOVES; ++i)
    {
        u16 move = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);
        if (move == MOVE_NONE)
            break;

        if (SPLIT(move) == SPLIT_STATUS)
            continue;

        move = GetMonMaxMoveByMove(mon, move);
        if (move != MOVE_NONE && gBattleMoves[move].argument == maxEffect)
            return TRUE;
    }

    return FALSE;
}

bool8 MoveInMonMovesetThatCanChangeByGigantamaxing(struct Pokemon* mon)
{
/*    if (mon->gigantamax)
    {
        for (u32 i = 0; i < MAX_MON_MOVES; ++i)
        {
            u16 move = GetMonData(mon, MON_DATA_MOVE1 + i, NULL);

            if (IsGMaxMove(GetMonMaxMove(mon, move)))
                return TRUE;
        }
    }*/

    return FALSE;
}

bool8 IsMockBattle(void)
{
    return FALSE;
}

bool8 BankSideHasTwoTrainers(u8 bank)
{
    u8 side = SIDE(bank);

    return ((side == B_SIDE_OPPONENT && IsTwoOpponentBattle())
            || (side == B_SIDE_PLAYER && IsTagBattle()));
}

bool8 AnyStatGreaterThan(u8 bank, u8 amount)
{
    u8 i;
    for (i = STAT_ATK; i < NUM_BATTLE_STATS; ++i)
    {
        if (STAT_STAGE(bank, i) > amount)
            return TRUE;
    }

    return FALSE;
}
u16 AI_TypeCalc(u16 move, u8 bankAtk, struct Pokemon* monDef)
{
    u32 i;
    u32 battlerDef = BATTLE_OPPOSITE(bankAtk);
    u16 flag;
    struct BattlePokemon *battleMons = Alloc(sizeof(struct BattlePokemon) * MAX_BATTLERS_COUNT);

    for (i = 0; i < MAX_BATTLERS_COUNT; i++)
        battleMons[i] = gBattleMons[i];

    PokemonToBattleMon(monDef, &gBattleMons[bankAtk]);
    flag = TypeCalc(move, bankAtk, battlerDef, NULL, FALSE);

    for (i = 0; i < MAX_BATTLERS_COUNT; i++)
        gBattleMons[i] = battleMons[i];

    Free(battleMons);

    return flag;
}

u8 GetMonMoveTypeSpecial(struct Pokemon* mon, u16 move)
{
    return gBattleMoves[move].type;
}

s8 PriorityCalcMon(struct Pokemon* mon, u16 move)
{
    u8 priority = 0;

    priority = gBattleMoves[move].priority;

    switch (GetMonAbility(mon)) {
        case ABILITY_PRANKSTER:
            if (SPLIT(move) == SPLIT_STATUS)
                ++priority;
            break;

        case ABILITY_GALE_WINGS:
            if (GetMonMoveTypeSpecial(mon, move) == TYPE_FLYING)
            {
#ifndef OLD_GALE_WINGS
                if (GetMonData(mon, MON_DATA_HP, NULL) == GetMonData(mon, MON_DATA_MAX_HP, NULL))
#endif
                    ++priority;
            }
            break;

        case ABILITY_TRIAGE:
            if (gBattleMoves[move].flags & FLAG_HIGH_CRIT)
                priority += 3;
    }

    return priority;
}

bool8 WillFaintFromEntryHazards(struct Pokemon* mon, u8 side)
{
    u16 hp = GetMonData(mon, MON_DATA_HP, NULL);
    u32 dmg = 0;

    if (gSideStatuses[side] & SIDE_STATUS_SPIKES
        && GetMonAbility(mon) != ABILITY_MAGIC_GUARD
        && ItemId_GetHoldEffect(GetMonData(mon, MON_DATA_HELD_ITEM, NULL)) != HOLD_EFFECT_HEAVY_DUTY_BOOTS)
    {
        if (gSideTimers[side].spikesAmount > 0)
            dmg += CalcStealthRockDamagePartyMon(mon);

        /*if (gSideTimers[side].steelsurge > 0)
            dmg += CalcSteelsurgeDamagePartyMon(mon);*/

        if (gSideTimers[side].spikesAmount > 0)
            dmg += CalcSpikesDamagePartyMon(mon, side);

        if (dmg >= hp)
            return TRUE;
    }

    return FALSE;
}


bool8 IsZCrystal(u16 item)
{
    return ItemId_GetHoldEffect(item) == HOLD_EFFECT_Z_CRYSTAL;
}

bool8 IsTypeZCrystal(u16 item, u8 moveType)
{
    return IsZCrystal(item) && ItemId_GetHoldEffectParam(item) == moveType && !IsSpecialZCrystal(item);
}

bool8 IsMegaZMoveBannedBattle(void)
{
    return FALSE;
}

u8 GetTerrainType()
{
    if (gFieldStatuses & STATUS_FIELD_GRASSY_TERRAIN)
        return GRASSY_TERRAIN;
    if (gFieldStatuses & STATUS_FIELD_MISTY_TERRAIN)
        return MISTY_TERRAIN;
    if (gFieldStatuses & STATUS_FIELD_ELECTRIC_TERRAIN)
        return ELECTRIC_TERRAIN;
    if (gFieldStatuses & STATUS_FIELD_PSYCHIC_TERRAIN)
        return PSYCHIC_TERRAIN;
    return 0xFF;
}

bool8 CanPartyMonBeGeneralStatused(struct Pokemon* mon)
{
    u16 species = GetMonData(mon, MON_DATA_SPECIES, NULL);
    u8 type1 = GetMonType(mon, 0);
    u8 type2 = GetMonType(mon, 1);

#ifdef SPECIES_MINIOR_SHIELD
    if (species == SPECIES_MINIOR_SHIELD)
		return FALSE;
#endif

    switch (GetMonAbility(mon)) {
        case ABILITY_COMATOSE:
            return FALSE;

        case ABILITY_FLOWER_VEIL:
            if (type1 == TYPE_GRASS
                ||  type2 == TYPE_GRASS)
                return FALSE;
    }

    return TRUE;
}

bool8 CanPartyMonBePoisoned(struct Pokemon* mon)
{
    u8 type1 = GetMonType(mon, 0);
    u8 type2 = GetMonType(mon, 1);

    if (!CanPartyMonBeGeneralStatused(mon))
        return FALSE;

    switch (GetMonAbility(mon)) {
        case ABILITY_IMMUNITY:
        case ABILITY_PASTEL_VEIL:
            return FALSE;
    }

    if (type1 == TYPE_POISON
        ||  type2 == TYPE_POISON
        ||  type1 == TYPE_STEEL
        ||  type2 == TYPE_STEEL)
        return FALSE;

    return TRUE;
}

bool8 IsAffectedByPowderByDetails(u8 type1, u8 type2, u8 type3, u8 ability, u8 itemEffect)
{
    return ability != ABILITY_OVERCOAT
           && itemEffect != HOLD_EFFECT_SAFETY_GOOGLES
           && type1 != TYPE_GRASS
           && type2 != TYPE_GRASS
           && type3 != TYPE_GRASS;
}

bool8 IsAffectedByPowder(u8 bank)
{
    return IsAffectedByPowderByDetails(gBattleMons[bank].type1, gBattleMons[bank].type2, gBattleMons[bank].type3, ABILITY(bank), ITEM_EFFECT(bank));
}

bool8 CheckTableForMoveEffect(u16 move, const u16 table[])
{
    return CheckTableForMove(gBattleMoves[move].effect, table);
}

bool8 IsMoveRedirectionPrevented(u16 move, u8 atkAbility)
{
    return move == MOVE_SKY_DROP
           || move == MOVE_SNIPE_SHOT
           //		|| atkAbility == ABILITY_PROPELLERTAIL
           || atkAbility == ABILITY_STALWART;
}


bool8 CantUseSoundMoves(u8 bank)
{
    return gDisableStructs[bank].throatChopTimer > 0;
}

bool8 IsMaxMoveWithStatusEffect(u16 move)
{
    if (IsAnyMaxMove(move))
    {
        switch (gBattleMoves[move].argument) {
            case MAX_EFFECT_EFFECT_SPORE_FOES:
            case MAX_EFFECT_POISON_FOES:
            case MAX_EFFECT_PARALYZE_FOES:
            case MAX_EFFECT_POISON_PARALYZE_FOES:
                return TRUE;
        }
    }

    return FALSE;
}

bool8 BankSideHasMist(u8 bank)
{
    return gSideStatuses[SIDE(bank)] & SIDE_STATUS_MIST;
}

bool8 IsMaxMoveWithEffect(u16 move, u8 effect)
{
    return IsAnyMaxMove(move) && gBattleMoves[move].argument == effect;
}

bool8 IsMaxMoveWithConfusionEffect(u16 move)
{
    if (IsAnyMaxMove(move))
    {
        switch (gBattleMoves[move].argument) {
            case MAX_EFFECT_CONFUSE_FOES:
            case MAX_EFFECT_CONFUSE_FOES_PAY_DAY:
                return TRUE;
        }
    }

    return FALSE;
}

bool8 IsLaserFocused(u8 bank)
{
    return gDisableStructs[bank].laserFocusTimer > 0;
}

bool8 IsMaxMoveWithWeatherEffect(u16 move)
{
    if (IsAnyMaxMove(move))
    {
        switch (gBattleMoves[move].argument) {
            case MAX_EFFECT_SUN:
            case MAX_EFFECT_RAIN:
            case MAX_EFFECT_SANDSTORM:
            case MAX_EFFECT_HAIL:
                return TRUE;
        }
    }

    return FALSE;
}


bool8 BankSideHasSafeguard(u8 bank)
{
    return gSideStatuses[SIDE(bank)] & SIDE_STATUS_SAFEGUARD;
}


bool8 IsIonDelugeActive(void)
{
    return  (gFieldStatuses & STATUS_FIELD_ION_DELUGE) != 0;
}

bool8 CanBeTormented(u8 bank)
{
    return !(gBattleMons[bank].status2 & STATUS2_TORMENT) && !IsDynamaxed(bank);
}

bool8 IsTaunted(u8 bank)
{
    return gDisableStructs[bank].tauntTimer > 0;
}

bool8 CheckTableForAbility(u16 ability, const u16 table[])
{
    return CheckTableForMove(ability, table);
}

bool8 CanFling(u16 item, u16 species, u8 ability, u8 bankOnSide, u8 embargoTimer)
{
    u8 itemEffect = ItemId_GetHoldEffect(item);

    if (item == ITEM_NONE
        || ability == ABILITY_KLUTZ
        || IsMagicRoomActive()
        || embargoTimer != 0
        || !CanLoseItem(species, item)
//        || itemEffect == HOLD_EFFECT_PRIMAL_ORB
        || itemEffect == HOLD_EFFECT_GEMS
//        || itemEffect == HOLD_EFFECT_ABILITY_CAPSULE
        || (IsBerry(item) && CheckBattlerAbility(bankOnSide, ABILITY_UNNERVE))
        || GetPocketByItemId(item) == POCKET_POKE_BALLS)
        return FALSE;

    return TRUE;
}

bool8 IsMudSportActive(void)
{
    return gFieldTimers.mudSportTimer > 0;
}

bool8 MainStatsMaxed(u8 bank)
{
    return AreStatsMaxed(bank, STAT_DEF);
}

bool8 IsMaxMoveWithTerrainEffect(u16 move)
{
    if (IsAnyMaxMove(move))
    {
        switch (gBattleMoves[move].argument) {
            case MAX_EFFECT_ELECTRIC_TERRAIN:
            case MAX_EFFECT_GRASSY_TERRAIN:
            case MAX_EFFECT_MISTY_TERRAIN:
            case MAX_EFFECT_PSYCHIC_TERRAIN:
                return TRUE;
        }
    }

    return FALSE;
}

bool8 IsWaterSportActive(void)
{
    return gFieldTimers.waterSportTimer > 0;
}

bool8 IsBankHoldingFocusSash(u8 bank)
{
    if (ITEM_EFFECT(bank) == HOLD_EFFECT_FOCUS_BAND
        && ItemId_GetUnknownValue(ITEM(bank)))
        return TRUE;

    return FALSE;
}

s32 CalcStrengthSapHealAmount(u8 bankAtk, u8 bankDef)
{
    u16 attack = gBattleMons[bankDef].attack;
    APPLY_QUICK_STAT_MOD(attack, STAT_STAGE(bankDef, STAT_ATK));
    attack = max(1, attack);

    if (ITEM_EFFECT(bankAtk) == HOLD_EFFECT_BIG_ROOT)
        attack = (13 * attack) / 10;

    return attack * -1;
}

bool8 IsHealBlocked(u8 battler)
{
    if (!(gStatuses3[battler] & STATUS3_HEAL_BLOCK))
        return FALSE;
    return TRUE;
}